/*
 *
 * C++ MaJiang for 血流红中
 *
 * TuyooGame, 2021
 * zhouxin@tuyoogame.com
 *
 */
#include <cmath>
#include <cstring>
#include <inttypes.h>
#include <iomanip>
#include "xueliuhz/card.h"
#include "hash_lookup3.h"

XueLiuHZCard::XueLiuHZCard() {
    state = NOT_READY;
    mfi_raw.hutype = DIAN_PAO;
    mfi_calfx.hutype = DIAN_PAO;
}

//参数：手牌，当前摸到的牌，癞子列表{35}，缺门，碰杠信息，红中个数
//如果没有now_tile(当前摸到的牌), nt传0
void
XueLiuHZCard::assign(vector<uint8_t> ts, uint8_t nt, vector<uint8_t> lz,
                     MJ_TILE_TYPE q, MJ_CPG_INFO *c, uint8_t hzcnt, MJ_HU_TYPE ht) {
    state = NOT_READY;
    if(assignMFI(&mfi_raw, ts, lz, nt, q, c, ht))
        state = READY;
    hongzhong_count = hzcnt;
}

XueLiuHZCard::XueLiuHZCard(vector<uint8_t> tiles, uint8_t now_tile, 
                           vector<uint8_t> laizi,
                           MJ_TILE_TYPE que, MJ_CPG_INFO *cpg, 
                           uint8_t hongzhong_count, MJ_HU_TYPE ht) {
    XueLiuHZCard();
    assign(tiles, now_tile, laizi, que, cpg, hongzhong_count, ht);
}

XueLiuHZCard::~XueLiuHZCard() {
}

vector<uint8_t>
XueLiuHZCard::getQueTiles() {
    vector<uint8_t> q {};
    MJ_COUNTER *c = &mfi_raw.ti_hand_now.counter[mfi_raw.que];
    for(uint8_t i=0; i<c->bucket_len; i++) {
        uint8_t cnt = c->bucket[i];
        if(cnt) {
            for(uint8_t k=0; k<cnt; k++)
                q.push_back(i+1+mfi_raw.que*10);
        }
    }
    return q;
}

vector<uint8_t>
XueLiuHZCard::getTingTilesUniq() {
    vector<uint8_t> q = getQueTiles();
    vector<uint8_t> t {};
    size_t qlen = q.size();
    if(qlen > 0) {
        return (qlen == 1) ? q : t;
    } else {
        for(uint8_t n=0; n<5; n++) {
            MJ_COUNTER *c = &mfi_raw.ti_hand_now.counter[n];
            for(uint8_t i=0; i<c->bucket_len; i++)
                if(c->bucket[i]) 
                    t.push_back(i+1+(n==4 ? 34 : n*10));
        }
        return t;
    }
}

const vector<uint8_t> &
XueLiuHZCard::getLeftTiles() {
    const static vector<uint8_t> ret[3] {
        {11,12,13,14,15,16,17,18,19,21,22,23,24,25,26,27,28,29,35},
        {1,2,3,4,5,6,7,8,9,21,22,23,24,25,26,27,28,29,35},
        {1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,35}
    };
    const static vector<uint8_t> ret_full {
        {1,2,3,4,5,6,7,8,9,
         11,12,13,14,15,16,17,18,19,
         21,22,23,24,25,26,27,28,29,35}
    };
    return mfi_raw.que>=0 ? ret[mfi_raw.que] : ret_full; 
}

uint32_t
XueLiuHZCard::hasTing() {
    if(state!=READY) return -1;
    struct timeval tvs,tve;
    gettimeofday(&tvs,NULL);

    //获取打掉可能听的牌(去重)
    auto tingv = getTingTilesUniq();
    uint8_t tlen = tingv.size();
    if(tlen == 0) 
        return -1;

    //剩下外面可能胡的牌
    const auto & leftv = getLeftTiles();

    //清空hu_result
    map<uint8_t, vector<MJ_FANDATA>>().swap(hu_result);

    //循环打掉后可能听牌的牌...
    for(uint8_t i=0; i<tlen; i++) {
        vector<uint8_t> ts {};
        uint8_t out_tile = tingv[i];

        //删掉可以打掉的牌...
        mfi_raw.ti_hand_now.getHands(ts, out_tile);

        //遍历可胡的牌...
        vector<uint8_t> res {}; 
        for(uint8_t n=0; n<leftv.size(); n++) {
            if(hasHu(ts, leftv[n]))
                res.push_back(leftv[n]);
        }
        if(res.size()==0)
            continue;

        vector<MJ_FANDATA> mjfans {};
        //对可胡牌算番...
        for(uint8_t k=0; k<res.size(); k++) {
            uint8_t in_tile = res[k];
            //ts是拷贝传参数，否则会被assignMFI改变...
            calcFanCount(in_tile, ts, mfi_raw.hutype, -1, mjfans);
        }
        hu_result[out_tile] = mjfans;
    }
    gettimeofday(&tve,NULL);
    uint32_t span = (uint32_t)((tve.tv_sec-tvs.tv_sec)*1000000 + (tve.tv_usec-tvs.tv_usec));
    return span;
}

void
XueLiuHZCard::calcFanCount(uint8_t in_tile, vector<uint8_t> ts, 
                           MJ_HU_TYPE h, int8_t seat, 
                           vector<MJ_FANDATA> &mjf) {
    vector<uint8_t> lz {35};
    assignMFI(&mfi_calfx, ts, lz, in_tile, mfi_raw.que, &mfi_raw.cpg, h);
    vector<MJ_FANXING> mfv {};
    map<MJ_FANXING, int> mfd {};
    int mfc = 0;
    //消除警告，seat暂时没有使用
    seat = seat + 0;
    getMaxFanData(&mfi_calfx, mfv, mfd, mfc); 
    MJ_FANDATA mf;
    mf.hutile = in_tile;
    mf.fancount = mfc;
    mf.fandata = mfv;
    mjf.push_back(mf);
}

inline bool 
XueLiuHZCard::hasKeFan(MJ_FANXING f) {
    if(f==SIANKE or f==SANANKE or f==SHUANGANKE or f==SANJIEGAO or f==SIJIEGAO)
        return true;
    return false;
}

inline bool 
XueLiuHZCard::canRmAllKe(MJ_FANXING f) {
    if(f==ZHUOWUKUI or f==SHUANGTONGKE or f==LAOSHAOPEI or 
       f==DANDIAO or f==YIBANGAO or f==LIULIANSHUN or 
       f==MENQING or f==BIANZHANG or f==KANZHANG) 
        return true;
    return false;
}

uint64_t 
XueLiuHZCard::checkKeTiles(MJ_FX_INFO *mfi, 
                           MJ_FANXING fan, 
                           vector<uint8_t> &ketiles, 
                           uint64_t ret_data, uint8_t start, bool rmove) {
    if(!hasKeFan(fan))
        return ret_data;
    vector<uint8_t> retv {};
    fxret2v(ret_data, retv);
    if(start==1) {
        catvv(ketiles, retv);
        return ret_data;
    }
    vector<uint8_t> remainKe {};
    for(auto r:retv) {
        if(!findv(ketiles, r)) 
            remainKe.push_back(r);
    }
    if(remainKe.size()==0) 
        return ret_data;
    vector<uint8_t> allke;
    if(fan == SHUANGANKE and rmove) {
        allke = remainKe;
    } else {
        catvv(allke, ketiles);
        catvv(allke, remainKe);
    }
    vector<uint8_t> cpremainke = allke;
    for(uint8_t i=0; i<mfi->cpg.count; i++) {
        uint8_t tile = mfi->cpg.cpgs[i].tile;
        if(!findv(cpremainke, tile))
            continue;
        remove_nv(cpremainke, tile, 1);
    }
    if(cpremainke.size()==0) {
        catvv(ketiles, remainKe);
        return ret_data;
    }
    uint8_t nowtile = mfi->now_tile;
    vector<uint8_t> cptiles {};
    mfi->ti_hand.getHands(cptiles);
    for(auto ke : cpremainke) {
        for(uint8_t i=0; i<3; i++) {
            if(_hand_nt(cptiles, nowtile, ke)==0)
                return 0;
        }
    }
    int8_t ret = hasHu(cptiles, nowtile);
    if(ret) {
        catvv(ketiles, remainKe);
        return ret_data;
    }
    return 0;
}


uint64_t 
XueLiuHZCard::checkZhuoWuKui(MJ_FX_INFO *mfi) {
    if(mfi) return 0;
    const vector<uint8_t> yitiaolong {1, 2, 3, 4, 5, 6, 7, 8, 9};
    vector<uint8_t> cptiles {};
    mfi->ti_hand_now.getHands(cptiles);
    for(auto t:yitiaolong) {
        if(findv(cptiles, t))
            remove_nv(cptiles, t, 1);
        else if(findv(cptiles, 35)) 
            remove_nv(cptiles, 35, 1);
        else
            return 1;
    }
    vector<uint8_t> wukui {1, 2, 3, 4, 6, 7, 8, 9};
    vector<uint8_t> cptiles2 {};
    mfi->ti_hand_now.getHands(cptiles2);
    for(auto c:wukui) {
        if(findv(cptiles2, c))
            remove_nv(cptiles2, c, 1);
        else if(findv(cptiles2, 35))
            remove_nv(cptiles2, 35, 1);
    }
    if(hasHu(cptiles2, 0))
        return 1;
    return 0;
}


uint64_t
XueLiuHZCard::checkYiTiaoLong(MJ_FX_INFO *mfi) {
    vector<uint8_t> yitiaolong {1, 2, 3, 4, 5, 6, 7, 8, 9};
    vector<uint8_t> cptiles {};
    mfi->ti_hand.getHands(cptiles);
    cptiles.push_back(mfi->now_tile);
    int8_t color = -1;
    for(auto c : cptiles) {
        if(c==35) continue;
        color = c / 10;
        break;
    }
    for(auto t : yitiaolong) {
        uint8_t t1 = color*10 + t;
        if(findv(cptiles, t))
            remove_nv(cptiles, t1, 1);
        else
            remove_nv(cptiles, 35, 1);
    }
    vector<uint8_t> no19 {2,3,4,5,6,7,8};
    for(auto c : cptiles) {
        if(findv(no19, c%10))
            return 0;
    }
    return 1;
}

uint64_t
XueLiuHZCard::checkShuangAnKe(MJ_FX_INFO *mfi, uint64_t ret_data) {
    vector<uint8_t> ketiles {};
    fxret2v(ret_data, ketiles);
    for(uint8_t i=0; i<mfi->cpg.count; i++) {
        uint8_t tile = mfi->cpg.cpgs[i].tile;
        if(!findv(ketiles, tile))
            continue;
        if(tile%10!=1 and tile%10!=9)
            return 0;
        remove_nv(ketiles, tile, 1);
    }
    uint8_t nowtile = mfi->now_tile;
    vector<uint8_t> cptiles {};
    mfi->ti_hand.getHands(cptiles);
    for(auto ke : ketiles) {
        if(ke%10!=1 and ke%10!=9)
            return 0;
        for(uint8_t i=0; i<3; i++) {
            if(_hand_nt(cptiles, nowtile, ke)==0)
                return 0;
        }
    }
    return fx_DuanYaoJiu(mfi);
}

void
XueLiuHZCard::getFanXing(MJ_FX_INFO *mfi, vector<vector<MJ_FANXING>> & fan_datas ) {
    map<MJ_FANXING, uint64_t> all_fan {};
    map<MJ_FANXING, uint64_t> cp_fan {};
    MJ_FX_INFO cp_mfi;

    uint8_t tile = mfi->now_tile;
    bool tins = mfi->ti_hand.findTile(tile);
    uint64_t qidui = fx_QiDui(mfi);

    bool is_qidui = (tile==35 or tins) and qidui>0;
    all_fan[QIDUI] = is_qidui and qidui==1;
    all_fan[LONGQIDUI] = is_qidui and qidui==10;
    all_fan[SHUANGLONGQIDUI] = is_qidui and qidui==20;
    all_fan[SANLONGQIDUI] = is_qidui and qidui==30;

    all_fan[BUQIUREN] = fx_MenQing(mfi) and mfi->hutype==ZI_MO;
    all_fan[MENQING] = fx_MenQing(mfi) and mfi->hutype!=ZI_MO;
    all_fan[DUANYAOJIU] = fx_DuanYaoJiu(mfi);
    all_fan[SUHU] = !mfi->ti_all.findTile(35);
    //printf("qidui2 = %d\n", qidui);

    for(auto fan : order_fan) {
        cp_fan = all_fan;
        //printf("qidui3 = %d\n", fan);
        //深拷贝MFI...
        cp_mfi = *mfi;

        vector<uint8_t> ketiles {};
        vector<MJ_FANXING> fan_data {};
        bool remove = false;
        //if(fan==QINGYISE)
        //    printf("DEBUG....\n");
        if(cp_fan.find(fan)==cp_fan.end()) {
            uint64_t ret = fxFuncMap.at(fan)(&cp_mfi);
            if(ret>0) {
                ret = checkKeTiles(&cp_mfi, fan, ketiles, ret, 1);
            }
            cp_fan[fan] = ret;
            if(ret>0)
                fan_data.push_back(fan);
            else
                continue;
        } else {
            if(cp_fan[fan])
                fan_data.push_back(fan);
            else 
                continue;
        }
        if(fanPatternRelationMatrix.find(fan)==fanPatternRelationMatrix.end()) {
            fan_datas.push_back(fan_data);
            continue;
        }
        if(fxRmFuncMap.find(fan)!=fxRmFuncMap.end()) 
            fxRmFuncMap.at(fan)(&cp_mfi, cp_fan[fan]);
        for(auto ex_fan : fanPatternRelationMatrix.at(fan)) {
            bool isbreak = false;
            for(auto al : fan_data) {
                const vector<MJ_FANXING> &fpal = fanPatternRelationMatrix.at(al);
                if(!findv(fpal, ex_fan)) {
                    isbreak = true;
                    break;
                }
            }
            if(!isbreak) {
                //if(fan==QINGYISE and ex_fan==SHUANGANKE)
                //    printf("DEBUG.....\n");
                if(canRmAllKe(ex_fan) and !remove) {
                    rm_KeTiles(&cp_mfi, ketiles);
                    remove = true;
                }
                if(cp_fan.find(ex_fan) == cp_fan.end()) {
                    uint64_t ret;
                    if(ex_fan == SHUANGTONGKE) {
                        vector<uint8_t> tmp {};
                        ret = fxFuncMap.at(ex_fan)(mfi);
                        if(ret) 
                           ret = rm_TongKeTiles(&cp_mfi, ketiles, ret);
                    } else if (fan==JIULIANBAODENG and ex_fan==YITIAOLONG) {
                        ret = checkYiTiaoLong(&cp_mfi);
                    } else {
                        //if(fan==QINGYISE && ex_fan==PENGPENGHU)
                        //    printf("DEBUG.....\n");
                        //if(fan==QINGYISE && ex_fan==LIULIANSHUN)
                        //    printf("DEBUG.....\n");
                        ret = fxFuncMap.at(ex_fan)(&cp_mfi);
                        if(ret and ex_fan==SHUANGANKE and findv(fan_data, DAIYAOJIU))
                            ret = checkShuangAnKe(&cp_mfi, ret);
                        else if (ret and ex_fan==ZHUOWUKUI and findv(fan_data, YITIAOLONG))
                            ret = checkZhuoWuKui(&cp_mfi);
                    }
                    if(ret) {
                        ret = checkKeTiles(&cp_mfi, ex_fan, ketiles, ret, 0, remove);
                    }
                    cp_fan[ex_fan] = ret;
                    if(ret)
                        fan_data.push_back(ex_fan);
                } else {
                    if(cp_fan[ex_fan]) {
                        if(ex_fan==SHUANGTONGKE) {
                            rm_TongKeTiles(&cp_mfi, ketiles, cp_fan[ex_fan]);
                        }
                        fan_data.push_back(ex_fan);
                    }
                }
                if(cp_fan[ex_fan] and fxRmFuncMap.find(ex_fan)!=fxRmFuncMap.end()) {
                    uint64_t ret = fxRmFuncMap.at(ex_fan)(&cp_mfi, cp_fan[ex_fan]);
                    if(!ret) {
                        cp_fan[ex_fan] = 0;
                    }
                }
            }
        }
        fan_datas.push_back(fan_data);
    }
    if(fan_datas.size()==0) {
        vector<MJ_FANXING> ph = {PINGHU};
        fan_datas.push_back(ph);
    }
}

void 
XueLiuHZCard::getCurrentFanCount(vector<MJ_FANXING> & fan_data, int gen_count, 
                                 map<MJ_FANXING, int> & fan_dict, int & fan_count) {
    fan_dict.clear();
    fan_count = 1;
    for(auto fan : fan_data) {
        if(fan==HUJIAOZHUANYI) {
            fan_dict[fan] = 1;
            continue;
        }
        int value = fanPattern.at(fan).value;
        if(value==0) {
            value = pow(2, gen_count);
            fan_dict[fan] = gen_count;
        } else {
            fan_dict[fan] = value;
        }
        fan_count *= value;
    }
}

void 
XueLiuHZCard::getMaxFanData(MJ_FX_INFO *mfi, vector<MJ_FANXING> & max_fan_data, 
                            map<MJ_FANXING, int> & max_fan_dict, int & max_fan_count) {
    vector<vector<MJ_FANXING>> fdata {};
    getFanXing(mfi, fdata);
    max_fan_count = 0;
    for(auto fd : fdata) {
        int gen = 0;
        bool isbreak = false;
        for(auto f : fd) {
            if(f==LONGQIDUI or f==SHUANGLONGQIDUI or f==SANLONGQIDUI 
               or f==SHIBALUOHAN or f==SHIERJINCHAI) {
                isbreak = true;
                break;
            }
        }
        if(!isbreak) {
            gen = getFanGenCount(mfi);
            if(gen>=1) {
                fd.push_back(GEN);
            }
        }
        map<MJ_FANXING, int> fan_dict {};
        int fan_count = 0;
        getCurrentFanCount(fd, gen, fan_dict, fan_count);
        if(fan_count > max_fan_count) {
            max_fan_data = fd;
            max_fan_dict = fan_dict;
            max_fan_count = fan_count;
        }
    }
}

//从网络请求等接收到的输入可以放到input_buffer，
//用这个方法解析，并assign给XueLiuHZCard对象
//以便下一步计算...
bool
XueLiuHZCard::loadInput() {
    input_size = strlen(input_buffer);
    string sinput = input_buffer;
    vector<string> v {};
    supersplit(sinput, v, ".");
    if(v.size() != 54) {
        state = NOT_READY;
        return false;
    }
    try {
        request_id = stoll(v[0]);
        uint8_t nowtile = stoi(v[1]);
        uint8_t tlen = stoi(v[2]);
        vector<uint8_t> ts {};
        for(uint8_t i=0; i<tlen; i++) {
            ts.push_back(stoi(v[3+i]));
        }
        uint8_t lz = 35;
        vector<uint8_t> lzv {};
        lzv.push_back(lz);

        MJ_TILE_TYPE que = (MJ_TILE_TYPE)stoi(v[18]);
        MJ_CPG_INFO cpg;
        cpg.count = stoi(v[19]); 
        for(uint8_t i=0; i<cpg.count; i++) {
            cpg.cpgs[i].tile = stoi(v[20+8*i]);
            for(uint8_t j=0; j<4; j++)
                cpg.cpgs[i].com[j] = stoi(v[21+8*i+j]);
            cpg.cpgs[i].from_seat = stoi(v[25+8*i]);
            cpg.cpgs[i].type = (MJ_CPG_TYPE)stoi(v[26+8*i]);
            cpg.cpgs[i].subtype = (MJ_CPG_SUBTYPE)stoi(v[27+8*i]);
        }
        MJ_HU_TYPE hutype = (MJ_HU_TYPE)stoi(v[52]); 
        uint8_t hzcnt = stoi(v[53]);
        //保证hands顺序...
        sort(ts.begin(), ts.end());
        assign(ts, nowtile, lzv, que, &cpg, hzcnt, hutype);
        if(state == READY)
            return true;
    } catch (std::invalid_argument& ex) {
        std::cout << "invalid_argument: " << ex.what() << "\n";
        state = NOT_READY;
        return false;
    } catch (std::exception& ex) {
        std::cout << "exception: " << ex.what() << "\n";
        state = NOT_READY;
        return false;
    }
    state = NOT_READY;
    return false;
}

//只有dumpInput需要使用的一个inline函数
inline void 
pbuf(char * &p, const char *fmt, uint8_t value, char *buf_hash, int &bhidx, bool rewrite) {
    if(rewrite) 
        p += sprintf(p, fmt, value);
    buf_hash[bhidx++] = value;
}

//整理input_buffer，并计算hash值
//如果rewrite为false，只计算hash，不重写input_buffer和input_size
//rewrite缺省为true
uint32_t
XueLiuHZCard::dumpInput(bool rewrite) {
    if(state!=READY) return 0;
    uint32_t r;
    char *p = input_buffer;
    
    //将不包含requestid的53个输入参数放到
    //buf_hash中，用hashlittle计算hash值
    char buf_hash[53];
    int  bhidx = 0;

    if(rewrite) {
        r = sprintf(p, "%" PRIu64 ".", request_id);
        p+=r;
    }
    pbuf(p, "%d.", mfi_raw.now_tile, buf_hash, bhidx, rewrite);
    pbuf(p, "%d.", mfi_raw.ti_hand.tiles_len, buf_hash, bhidx, rewrite);

    //排序手牌，保证input中的tile是有序的，
    //便于后续以input做缓存键值
    vector<uint8_t> hand {};
    mfi_raw.ti_hand.getHands(hand);
    sort(hand.begin(), hand.end());

    for(uint8_t i=0; i<14; i++) {
        uint8_t t = 0;
        if(i<mfi_raw.ti_hand.tiles_len)
            t = hand[i];
         pbuf(p, "%d.", t, buf_hash, bhidx, rewrite);
    }
    pbuf(p, "%d.", 35, buf_hash, bhidx, rewrite);
    pbuf(p, "%d.", mfi_raw.que, buf_hash, bhidx, rewrite);

    auto pc = &mfi_raw.cpg;
    pbuf(p, "%d.", pc->count, buf_hash, bhidx, rewrite);

    //处理吃碰杠,32个整数...
    for(uint8_t i=0; i<4; i++) {
        uint8_t ct;
        ct = i>pc->count-1?0:pc->cpgs[i].tile;
        pbuf(p, "%d.", ct, buf_hash, bhidx, rewrite);
        for(uint8_t j=0; j<4; j++) {
            ct = i>pc->count-1?0:pc->cpgs[i].com[j];
            pbuf(p, "%d.", ct, buf_hash, bhidx, rewrite);
        }
        ct = i>pc->count-1?0:pc->cpgs[i].from_seat;
        pbuf(p, "%d.", ct, buf_hash, bhidx, rewrite);
        ct = i>pc->count-1?0:pc->cpgs[i].type;
        pbuf(p, "%d.", ct, buf_hash, bhidx, rewrite);
        ct = i>pc->count-1?0:pc->cpgs[i].subtype;
        pbuf(p, "%d.", ct, buf_hash, bhidx, rewrite);
    }

    pbuf(p, "%d.", hongzhong_count, buf_hash, bhidx, rewrite);
    pbuf(p, "%d.", mfi_raw.hutype, buf_hash, bhidx, rewrite);

    //去掉最后一个"."
    *--p=0;

    if(rewrite) input_size = p-input_buffer;
    uint32_t hashcode = hashlittle(buf_hash, 53, 760313);
    //printf("MFIHash: %x\n", hashcode);
    return hashcode;
}

//将运算结果写入output_buffer
//为了便于文本方式文件load,dump不包含\n和空格
//用-代替\n 用.代替空格
//参考mj_cache.cc的load_cache和dump_cache
void
XueLiuHZCard::dumpOutput() {
    if(state!=READY) return;
    char *p = output_buffer;
    p += sprintf(p, "request_id.%" PRIu64 ".out_tile_count.%lu-", request_id, hu_result.size());
    for(auto ot : hu_result) {
        p += sprintf(p, "out_tile.%d-", ot.first);
        for(auto mfd : hu_result[ot.first]) {
            p += sprintf(p, "%d.", mfd.hutile);
            p += sprintf(p, "%d.", mfd.fancount);
            for(auto fx : mfd.fandata) {
                //为了节省通信量，用整数番型，没用字符串番型
                //需要同步好fanPattern列表，避免对不上
                //r = sprintf(p, "%s ", fanPattern.at(fx).eng_name);
                p += sprintf(p, "%d.", fx);
            }
            p += sprintf(p, "-");
        }
    }
    output_size = p-output_buffer;
}

void 
XueLiuHZCard::printHuResult() {
    char *buf = output_buffer;
    vector<string> lv {};
    vector<string> wv {};
    supersplit(string(buf), lv, "-");
    for(auto l : lv) {
        wv.clear();
        supersplit(l, wv, ".");
        if(wv[0]=="request_id") {
            cout << wv[3] << "张牌打掉后可听" << endl;
        } else if (wv[0] == "out_tile") {
            cout << "out:" << wv[1] << endl;
        } else {
            cout << setw(2) << wv[0] << " " << setw(4) << wv[1] << " ";
            for(unsigned int i=0; i<wv.size()-2; i++) {
                cout << fanPattern.at((MJ_FANXING)stol(wv[i+2])).cn_name << " ";
            }
            cout << endl;
        }
    }
    cout << endl;
}

